﻿#include "game_object.h"

static bool checkCollision(const SDL_FRect &a, const SDL_FRect &b);
static bool checkStanding(const SDL_FRect &a, const SDL_FRect &b);
static float distance(const SDL_FRect& a, const SDL_FRect& b);

GameObject::GameObject(int x, int y, int width, int height, bool isMovable) : realX(static_cast<float>(x)), realY(static_cast<float>(y)),
                                                                              nextX(realX), nextY(realY),
                                                                              width(width), height(height),
                                                                              isMovable(isMovable)
{
}

void GameObject::beforeHandleCollision(const std::vector<GameObject *> &platforms)
{
    nextX = realX + velocityX;
    nextY = realY - velocityY;
}

void GameObject::moveAndHandleCollision(const std::vector<GameObject *> &platforms)
{
    bool onPlatform = false;
    for (const auto &obj : platforms)
    {
        if (obj == this) // 避免与自身发生碰撞
        {
            continue;
        }

        if (checkCollision(getNextRect(), obj->getRealRect())) // 撞别人
        {
            // 水平碰撞检测
            // 向右
            if (velocityX > 0)
            {
                nextX = realX;
                if (nextX < obj->realX)
                {
                    // 靠太近了，被往回顶
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextX = obj->realX - width;
                    }
                }
                else
                {
                    nextX = obj->realX + obj->width;
                    continue;
                }
                if (obj->isMovable)
                {
                    obj->velocityX = BOX_SPEED;
                }
                velocityX = -velocityX;
            }
            // 向左
            if (velocityX < 0)
            {
                nextX = realX;
                if (nextX > obj->realX)
                {
                    // 靠太近了，被往回顶
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextX = obj->realX + obj->width;
                    }
                }
                else
                {
                    nextX = obj->realX - width;
                    continue;
                }
                if (obj->isMovable)
                {
                    obj->velocityX = -BOX_SPEED;
                }
                velocityX = -velocityX;
            }

            // 垂直碰撞检测
            if (velocityY > 0)
            {
                nextY = realY;
                if (nextY < obj->realY)
                {
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextY = obj->realY - height;
                    }
                    velocityY = 0;
                    onPlatform = true;
                }
                else
                {
                    nextY = obj->realY + obj->height;
                    velocityY = 0;
                    onPlatform = true;
                    continue;
                }
            }
            if (velocityY < 0)
            {
                nextY = realY;
                if (nextY > obj->realY)
                {
                    if (distance(getNextRect(), obj->getRealRect()) <= DISTANCE_MIN)
                    {
                        nextY = obj->realY + obj->height;
                    }
                    velocityY = 0;
                    onPlatform = true;
                }
                else
                {
                    nextY = obj->realY - height;
                    velocityY = 0;
                    onPlatform = true;
                    continue;
                }
            }
        }

        // 站在物体上检测
        if (checkStanding(getNextRect(), obj->getRealRect()) &&
            !checkCollision(getNextRect(), obj->getRealRect()))
        {
            nextY = obj->realY - height;
            velocityY = 0;
            onPlatform = true;
        }
    }

    // 屏幕边界检测
    // 左边界检测
    if (nextX < 0)
    {
        nextX = 0;
        velocityX = -velocityX;
    }
    // 右边界检测
    if (nextX > SCREEN_WIDTH - width)
    {
        nextX = SCREEN_WIDTH - width;
        velocityX = -velocityX;
    }

    // 触地检测
    bool onGround = false;
    float groundLevel = static_cast<float>(GROUND_HEIGHT - height);
    if (nextY >= groundLevel)
    {
        nextY = groundLevel;
        velocityY = 0;
        onGround = true;
    }

    if (onPlatform ||
        onGround)
    {
        isJumping = false;
    }
    else
    {
        isJumping = true;
    }
}

void GameObject::afterHandleCollision(const std::vector<GameObject *> &platforms)
{
    realX = nextX;
    realY = nextY;

    if (isJumping)
    {
        velocityY -= GRAVITY;
    }
}

void GameObject::handleInput(const bool* keystates)
{}

void GameObject::beforeUpdate(const bool *keystates, const std::vector<GameObject *> &platforms)
{
    beforeHandleCollision(platforms);
}

void GameObject::update(const bool *keystates, const std::vector<GameObject *> &platforms)
{
    moveAndHandleCollision(platforms);
}

void GameObject::afterUpdate(const bool *keystates, const std::vector<GameObject *> &platforms)
{
    afterHandleCollision(platforms);
}

void GameObject::jump()
{
    if (!isJumping)
    {
        isJumping = true;
        velocityY = JUMP_STRENGTH;
    }
}

SDL_FRect GameObject::getRealRect() const
{
    return {realX, realY, static_cast<float>(width), static_cast<float>(height)};
}

SDL_FRect GameObject::getNextRect() const
{
    return {nextX, nextY, static_cast<float>(width), static_cast<float>(height)};
}

// 简单的碰撞检测
bool checkCollision(const SDL_FRect &a, const SDL_FRect &b)
{
    // 计算矩形A的边
    float leftA = a.x;
    float rightA = a.x + a.w;
    float topA = a.y;
    float bottomA = a.y + a.h;

    // 计算矩形B的边
    float leftB = b.x;
    float rightB = b.x + b.w;
    float topB = b.y;
    float bottomB = b.y + b.h;

    // 检查是否有分离轴
    if (bottomA <= topB)
        return false; // A在B的上方
    if (topA >= bottomB)
        return false; // A在B的下方
    if (rightA <= leftB)
        return false; // A在B的左侧
    if (leftA >= rightB)
        return false; // A在B的右侧

    // 如果以上条件都不满足，则发生碰撞
    return true;
}

// 站在物体上检测
bool checkStanding(const SDL_FRect &a, const SDL_FRect &b)
{
    // 计算矩形A的边
    float leftA = a.x;
    float rightA = a.x + a.w;
    float topA = a.y;
    float bottomA = a.y + a.h;

    // 计算矩形B的边
    float leftB = b.x;
    float rightB = b.x + b.w;
    float topB = b.y;
    float bottomB = b.y + b.h;

    // 检查是否有分离轴
    if (bottomA < topB)
        return false; // A在B的上方，且非接触
    if (topA >= bottomB)
        return false; // A在B的下方
    if (rightA <= leftB)
        return false; // A在B的左侧
    if (leftA >= rightB)
        return false; // A在B的右侧

    // 如果以上条件都不满足，则发生碰撞
    return true;
}

float distance(const SDL_FRect& a, const SDL_FRect& b)
{
    // 计算矩形 a 的中心点
    float a_center_x = a.x + a.w / 2.0f;
    float a_center_y = a.y + a.h / 2.0f;

    // 计算矩形 b 的中心点
    float b_center_x = b.x + b.w / 2.0f;
    float b_center_y = b.y + b.h / 2.0f;

    // 计算两个中心点之间的距离
    float dx = a_center_x - b_center_x;
    float dy = a_center_y - b_center_y;
    return sqrtf(dx * dx + dy * dy);
}